// ==UserScript==
// @name         New Userscript
// @namespace    http://tampermonkey.net/
// @version      2025-02-09
// @description  try to take over the world!
// @author       You
// @match        https://www.zhihu.com/question/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=0.1
// @require      https://scriptcat.org/lib/2691/1.0.0/sweetalert2.all.min-11.15.10.js
// @require      https://scriptcat.org/lib/2724/1.0.0/js%20%E5%AD%97%E7%AC%A6%E4%B8%B2%E5%8E%8B%E7%BC%A9%E5%BA%93-LZString.js
// @grant        GM.setValue
// @grant        GM.getValue
// @grant        GM.deleteValue
// @grant        unsafeWindow
// @run-at       document-body
// ==/UserScript==

(function () {
    'use strict';

    class RequestMonitor {
        constructor() {
            // 预编译正则表达式提升匹配性能
            this._patterns = new Map(); // Map<RegExp, Processor>

            // 使用WeakMap防止内存泄漏
            this.pendingRequests = new WeakMap();

            // 缓存已验证的URL
            this._urlCache = {
                monitored: new Set(),
                ignored: new Set()
            };

            // 性能统计
            this.stats = {
                total: 0,
                processed: 0,
                errors: 0
            };

            this._initHooks();
        }
        /* pattern:正则表达式(只要满足其一就会进行监听处理)，processor：处理器函数(对满足的每一个pattern进行processor处理，一对多) */
        registerHandler(pattern, processor) {
            const regex = new RegExp(pattern);
            this._patterns.set(regex, processor);
            return this; // 支持链式调用
        }

        _initHooks() {
            // 保存原生方法引用
            const nativeFetch = unsafeWindow.fetch;
            const nativeXHROpen = XMLHttpRequest.prototype.open;
            const nativeXHRSend = XMLHttpRequest.prototype.send;

            const self = this;

            // Hook XHR open方法捕获URL
            XMLHttpRequest.prototype.open = function (method, url) {
                this._requestURL = url;
                return nativeXHROpen.apply(this, arguments);
            };

            // Hook XHR send
            XMLHttpRequest.prototype.send = function (body) {
                const xhr = this;
                const url = xhr._requestURL || '';

                // 前置检查
                if (!self._shouldMonitor(url)) {
                    return nativeXHRSend.apply(this, arguments);
                }

                const requestKey = {};
                self.pendingRequests.set(xhr, requestKey);
                self.stats.total++;

                // 添加事件监听
                xhr.addEventListener('load', () => self._handleXHRResponse(xhr, requestKey));
                xhr.addEventListener('error', () => self._cleanup(xhr));

                nativeXHRSend.apply(this, arguments);
            };

            // Hook Fetch
            unsafeWindow.fetch = async (input, init) => {
                const url = typeof input === 'string' ? input : input.url;

                if (!self._shouldMonitor(url)) {
                    return nativeFetch(input, init);
                }

                const requestKey = {};
                self.pendingRequests.set(requestKey, true);
                self.stats.total++;

                try {
                    const response = await nativeFetch(input, init);
                    self._handleFetchResponse(url, response.clone(), requestKey);
                    return response;
                } catch (error) {
                    self.stats.errors++;
                    self._cleanup(requestKey);
                    throw error;
                }
            };
        }

        _shouldMonitor(url) {
            if (this._urlCache.monitored.has(url)) return true;
            if (this._urlCache.ignored.has(url)) return false;

            for (const [regex] of this._patterns) {
                if (regex.test(url)) {
                    this._urlCache.monitored.add(url);
                    return true;
                }
            }

            this._urlCache.ignored.add(url);
            return false;
        }

        async _handleXHRResponse(xhr, requestKey) {
            try {
                const data = await this._parseResponse(xhr.response);
                await this._processData(xhr._requestURL, data);
                this.stats.processed++;
            } catch (error) {
                this.stats.errors++;
                console.error('XHR处理失败:', error);
            } finally {
                this._cleanup(xhr);
            }
        }

        async _handleFetchResponse(url, response, requestKey) {
            try {
                const data = await this._parseResponse(await response.text());
                await this._processData(url, data);
                this.stats.processed++;
            } catch (error) {
                this.stats.errors++;
                console.error('Fetch处理失败:', error);
            } finally {
                this._cleanup(requestKey);
            }
        }

        async _processData(url, rawData) {
            for (const [regex, processor] of this._patterns) {
                if (regex.test(url)) {
                    await processor(rawData);
                    break; // 仅匹配第一个处理器
                }
            }
        }

        async _parseResponse(data) {
            try {
                return JSON.parse(data);
            } catch {
                return data;
            }
        }

        _cleanup(target) {
            this.pendingRequests.delete(target);

            // 定期清理缓存防止内存增长
            if (this._urlCache.monitored.size > 1000) {
                this._urlCache.monitored.clear();
            }
        }

        // 调试方法
        printStats() {
            console.table({
                '总请求数': this.stats.total,
                '已处理请求': this.stats.processed,
                '当前挂起请求': this.pendingRequests.size,
                '错误数量': this.stats.errors,
                '缓存命中率': `${(this.stats.processed / this.stats.total * 100).toFixed(1)}%`
            });
        }
    }

    // storage-manager.js
    class StorageManager {
        constructor(options = {}) {
            // 配置参数
            this.namespace = options.namespace || 'defaultStorage';
            this.chunkSize = options.chunkSize || 500;      // 每个分片最大记录数
            this.maxChunks = options.maxChunks || 10;       // 最大保留分片数
            this.autoCleanThreshold = options.autoCleanThreshold || 5 * 1024 * 1024; // 5MB自动清理阈值
            this.compression = options.compression !== false; // 默认启用压缩 明确设置为 false 时，this.compression 才会被设置为 false

            // 状态跟踪
            this.currentChunk = 1;
            this.totalRecords = 0;
            this.lastCleanTime = 0;
        }

        // ======================== 核心接口 ========================
        /*  状态跟踪属性赋值 */
        async init() {
            await this._loadIndex();
            return this;
        }

        async save(data) {
            // 1. 获取当前分片
            let chunk = await this._getCurrentChunk();

            // 2. 检查分片容量
            if (chunk.length >= this.chunkSize) {
                // 更新状态跟踪属性
                await this._rotateChunk();
                chunk = [];
            }

            // 3. 添加数据
            chunk.push(data);
            this.totalRecords++;

            // 4. 保存分片
            await this._saveChunk(chunk);

            // 5. 自动清理检查
            // await this._autoCleanCheck();
        }

        async loadAll() {
            const chunks = [];
            for (let i = 1; i <= this.maxChunks; i++) {
                const chunk = await this._getChunk(i);
                if (chunk) chunks.push(...chunk);
            }
            // return this._deduplicate(chunks);
            return chunks;
        }

        async clear() {
            for (let i = 1; i <= this.maxChunks; i++) {
                await GM.deleteValue(this._chunkKey(i));
            }
            await this._saveIndex({ currentChunk: 1, totalRecords: 0 });
        }

        // ======================== 内部方法 ========================
        async _loadIndex() {
            const index = await GM.getValue(this._indexKey(), '{}');
            const { currentChunk = 1, totalRecords = 0 } = JSON.parse(index);
            this.currentChunk = currentChunk;
            this.totalRecords = totalRecords;
        }

        async _saveIndex() {
            const index = JSON.stringify({
                currentChunk: this.currentChunk,
                totalRecords: this.totalRecords,
                lastCleanTime: this.lastCleanTime
            });
            await GM.setValue(this._indexKey(), index);
        }

        async _getCurrentChunk() {
            return await this._getChunk(this.currentChunk) || [];
        }

        async _rotateChunk() {
            this.currentChunk = (this.currentChunk % this.maxChunks) + 1;
            await this._saveIndex();
        }

        async _saveChunk(chunk) {
            const data = this.compression
                ? LZString.compressToUTF16(JSON.stringify(chunk))
                : JSON.stringify(chunk);

            await GM.setValue(
                this._chunkKey(this.currentChunk),
                data
            );
        }

        async _getChunk(number) {
            const data = await GM.getValue(this._chunkKey(number));
            if (!data) return null;

            try {
                return JSON.parse(
                    this.compression
                        ? LZString.decompressFromUTF16(data)
                        : data
                );
            } catch (e) {
                console.error('分片数据解析失败:', e);
                return null;
            }
        }

        async _autoCleanCheck() {
            // 按时间清理（每10分钟）
            if (Date.now() - this.lastCleanTime > 600_000) {
                await this.cleanOldChunks();
                return;
            }

            // 按容量清理
            const size = await this._estimateStorageSize();
            if (size > this.autoCleanThreshold) {
                await this.cleanOldChunks();
                this.maxChunks = Math.max(3, Math.floor(this.maxChunks * 0.8));
            }
        }

        async cleanOldChunks() {
            const oldest = this.currentChunk > this.maxChunks
                ? this.currentChunk - this.maxChunks
                : 1;

            for (let i = 1; i <= oldest; i++) {
                await GM.deleteValue(this._chunkKey(i));
            }

            this.lastCleanTime = Date.now();
            await this._saveIndex();
        }

        async _estimateStorageSize() {
            let total = 0;
            for (let i = 1; i <= this.maxChunks; i++) {
                const data = await GM.getValue(this._chunkKey(i));
                total += data ? data.length : 0;
            }
            return total;
        }

        _deduplicate(data) {
            const seen = new Set();
            return data.filter(item => {
                const key = item.id || JSON.stringify(item);
                return seen.has(key) ? false : seen.add(key);
            });
        }

        // ======================== 工具方法 ========================
        _indexKey() {
            return `${this.namespace}_index`;
        }

        _chunkKey(number) {
            return `${this.namespace}_chunk_${number}`;
        }

        // ======================== 调试接口 ========================
        async printStats() {
            const size = await this._estimateStorageSize();
            console.log(`[StorageManager 状态报告]
       命名空间: ${this.namespace}
       当前分片: ${this.currentChunk}/${this.maxChunks}
       总记录数: ${this.totalRecords}
       预估存储: ${(size / 1024).toFixed(1)}KB
       最后清理: ${new Date(this.lastCleanTime).toLocaleTimeString()}
       压缩状态: ${this.compression ? '启用' : '关闭'}
      `);
        }
    }


    // const storageTest = new StorageManager({
    //     namespace: 'forumData',
    //     chunkSize: 1000,
    //     maxChunks: 20
    // }).init();
    // const monitorTest = new RequestMonitor();
    // monitorTest.registerHandler('api/v4/comment_v5', async data => {
    //     console.log(data);
    // });
    // window.addEventListener('load', async () => {
    //     await storageTest.clear();
    //     await storageTest.save({ test: 'test' });
    //     await storageTest.save({ test: 't*est12./?' });
    //     console.log(await storageTest.loadAll());
    //     await storageTest.printStats();
    // });


})();